#include "sdk.h"
#include <wx/dynlib.h>
#include <wx/filesys.h>
#include <wx/progdlg.h>
#include <wx/utils.h>
#include <wx/filename.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <wx/txtstrm.h>
#include "filefilters.h"
#include "tinywxuni.h"
#include <tinyxml.h>
#include "pluginsconfigurationdlg.h"
#include "scripting/bindings/sc_plugin.h"

template<> PluginManager* Mgr<PluginManager>::instance = nullptr;
template<> bool  Mgr<PluginManager>::isShutdown = false;

namespace LibLoader {
  struct RefCountedLib {
    RefCountedLib() : lib( nullptr ), ref( 0 ) {}
    wxDynamicLibrary* lib;
    int ref;
  };
  typedef std::map<wxString, RefCountedLib> Libs;
  Libs s_Libs;

  inline wxDynamicLibrary* LoadLibrary( const wxString& filename ) {
    Libs::iterator it = s_Libs.find( filename );
    if( it != s_Libs.end() ) {
      it->second.ref++;
      return it->second.lib;
    }
    it = s_Libs.insert( s_Libs.end(), std::make_pair( filename, RefCountedLib() ) );
    it->second.lib = new wxDynamicLibrary;
    it->second.ref = 1;
    it->second.lib->Load( filename );
    return it->second.lib;
  }

  inline void RemoveLibrary( wxDynamicLibrary* lib ) {
    Libs::iterator it;
    for( it = s_Libs.begin(); it != s_Libs.end(); ++it ) {
      RefCountedLib& rcl = it->second;
      if( rcl.lib == lib ) {
        rcl.ref--;
        if( rcl.ref == 0 ) {
          if( !Manager::IsAppShuttingDown() ) {
            delete rcl.lib;
          }
          s_Libs.erase( it );
        }
        return;
      }
    }
  }

  inline void Cleanup() {
    Libs::iterator it;
    for( it = s_Libs.begin(); it != s_Libs.end(); ++it ) {
      RefCountedLib& rcl = it->second;
      if( !Manager::IsAppShuttingDown() ) {
        delete rcl.lib;
      }
    }
    s_Libs.clear();
  }
};


bool PluginManager::s_SafeMode = false;

BEGIN_EVENT_TABLE( PluginManager, wxEvtHandler )
END_EVENT_TABLE()

PluginManager::PluginManager()
  : m_pCurrentlyLoadingLib( nullptr ),
    m_pCurrentlyLoadingManifestDoc( nullptr ) {
  Manager::Get()->GetAppWindow()->PushEventHandler( this );
}

PluginManager::~PluginManager() {
  UnloadAllPlugins();
}

void PluginManager::CreateMenu( M_Code_unused wxMenuBar* menuBar ) {
}

void PluginManager::ReleaseMenu( M_Code_unused wxMenuBar* menuBar ) {
}

bool PluginManager::AttachPlugin( M_Code_Plugin* plugin, bool ignoreSafeMode ) {
  if( !plugin ) {
    return false;
  }
  if( plugin->IsAttached() ) {
    return true;
  }
  if( !s_SafeMode || ignoreSafeMode ) {
    plugin->Attach();
  }
  return true;
}

bool PluginManager::DetachPlugin( M_Code_Plugin* plugin ) {
  if( !plugin ) {
    return false;
  }
  if( !plugin->IsAttached() ) {
    return true;
  }
  Manager::Get()->RemoveAllEventSinksFor( plugin );
  plugin->Release( Manager::IsAppShuttingDown() );
  return true;
}

bool PluginManager::InstallPlugin( const wxString& pluginName, bool forAllUsers, bool askForConfirmation ) {
  if( pluginName.IsEmpty() ) {
    return false;
  }
  wxString actualName = pluginName;
  Manager::Get()->GetMacrosManager()->ReplaceMacros( actualName );
  wxString basename = wxFileName( actualName ).GetName();
  if( basename.Contains( _T( '-' ) ) ) {
    basename = basename.BeforeFirst( _T( '-' ) );
  }
  M_Code_Plugin* existingPlugin = FindPluginByName( basename );
  if( existingPlugin ) {
    if( askForConfirmation ) {
      wxString msg = _T( "已安装同名插件替换插件?" );
      if( msging( msg, _T( "审查" ), wxICON_QUESTION | wxYES_NO ) == wxID_NO ) {
        return false;
      }
    }
    if( !UninstallPlugin( existingPlugin ) ) {
      return false;
    }
  }
  wxString pluginDir;
  wxString resourceDir;
  pluginDir = ConfigManager::GetFolder( sdPluginsGlobal );
  resourceDir = ConfigManager::GetFolder( sdDataGlobal );
  wxProgressDialog pd( _T( "安装: " ) + basename, _T( "对对话框足够宽的描述 ;)" ), 5 );
  wxString localName = basename + FileFilters::DYNAMICLIB_DOT_EXT;
  wxString resourceName = basename + _T( ".zip" );
  wxString settingsOnName = basename + _T( ".png" );
  wxString settingsOffName = basename + _T( "-off.png" );
  if( true && resourceName.StartsWith( _T( "lib" ) ) ) {
    resourceName.Remove( 0, 3 );
  }
  if( true && settingsOnName.StartsWith( _T( "lib" ) ) ) {
    settingsOnName.Remove( 0, 3 );
  }
  if( true && settingsOffName.StartsWith( _T( "lib" ) ) ) {
    settingsOffName.Remove( 0, 3 );
  }
  wxString pluginFilename = UnixFilename( pluginDir + _T( '/' ) + localName );
  pd.Update( 1, _T( "正在提取插件" ) );
  if( !ExtractFile( actualName, localName, pluginFilename ) ) {
    return false;
  }
  pd.Update( 2, _T( "正在提取插件资源" ) );
  if( !ExtractFile( actualName, resourceName, resourceDir + _T( '/' ) + resourceName ) ) {
    return false;
  }
  pd.Update( 3, _T( "正在提取“设置”对话框的插件图标" ) );
  ExtractFile( actualName, settingsOnName, resourceDir + _T( "/images/settings/" ) + settingsOnName, false );
  ExtractFile( actualName, settingsOffName, resourceDir + _T( "/images/settings/" ) + settingsOffName, false );
  pd.Update( 4, _T( "加载插件" ) );
  ScanForPlugins( pluginDir );
  LoadAllPlugins();
  M_Code_Plugin* plugin = FindPluginByFileName( pluginFilename );
  const PluginInfo* info = GetPluginInfo( plugin );
  if( !plugin || !info ) {
    Manager::Get()->GetLogManager()->LogError( _T( "失败" ) );
    return false;
  }
  pd.Update( 5, _T( "更新菜单和工具栏" ) );
  CodeBlocksEvent evt( cbEVT_PLUGIN_INSTALLED );
  evt.SetPlugin( plugin );
  Manager::Get()->ProcessEvent( evt );
  return true;
}

bool PluginManager::UninstallPlugin( M_Code_Plugin* plugin, bool removeFiles ) {
  if( !plugin ) {
    return false;
  }
  wxString title;
  wxString pluginFilename;
  wxString resourceFilename;
  wxString settingsOnFilename;
  wxString settingsOffFilename;
  for( size_t i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* elem = m_Plugins[i];
    if( elem && elem->plugin == plugin ) {
      title = elem->info.title;
      pluginFilename = elem->fileName;
      wxFileName fname( pluginFilename );
      resourceFilename = fname.GetName() + _T( ".zip" );
      settingsOnFilename = fname.GetName() + _T( ".png" );
      settingsOffFilename = fname.GetName() + _T( "-off.png" );
      if( true && resourceFilename.StartsWith( _T( "lib" ) ) ) {
        resourceFilename.Remove( 0, 3 );
      }
      if( true && settingsOnFilename.StartsWith( _T( "lib" ) ) ) {
        settingsOnFilename.Remove( 0, 3 );
      }
      if( true && settingsOffFilename.StartsWith( _T( "lib" ) ) ) {
        settingsOffFilename.Remove( 0, 3 );
      }
      resourceFilename = ConfigManager::LocateDataFile( resourceFilename, sdDataGlobal | sdDataUser );
      settingsOnFilename = ConfigManager::LocateDataFile( _T( "images/settings/" ) + settingsOnFilename, sdDataGlobal | sdDataUser );
      settingsOffFilename = ConfigManager::LocateDataFile( _T( "images/settings/" ) + settingsOffFilename, sdDataGlobal | sdDataUser );
      break;
    }
  }
  if( wxFileExists( pluginFilename ) && !wxFile::Access( pluginFilename, wxFile::write ) ) {
    msging( _T( "没有卸载此插件的权限" ), _T( "警告" ), wxICON_WARNING );
    return false;
  }
  wxProgressDialog pd( F( _T( "卸载 %s" ), title.c_str() ), _T( "对对话框足够宽的描述 ;)" ), 3 );
  pd.Update( 1, _T( "正在分离插件" ) );
  DetachPlugin( plugin );
  pd.Update( 2, _T( "更新菜单和工具栏" ) );
  CodeBlocksEvent event( cbEVT_PLUGIN_UNINSTALLED );
  event.SetPlugin( plugin );
  Manager::Get()->ProcessEvent( event );
  pd.Update( 3, _T( "卸载插件" ) );
  UnloadPlugin( plugin );
  if( !removeFiles ) {
    return true;
  }
  if( !pluginFilename.IsEmpty() ) {
    if( wxRemoveFile( pluginFilename ) ) {
      if( !resourceFilename.IsEmpty() ) {
        if( !wxRemoveFile( resourceFilename ) ) {
          Manager::Get()->GetLogManager()->LogWarning( _T( "删除插件资源失败: " ) + resourceFilename );
        }
      }
      if( !settingsOnFilename.IsEmpty() && wxFileExists( settingsOnFilename ) ) {
        if( !wxRemoveFile( settingsOnFilename ) ) {
          Manager::Get()->GetLogManager()->LogWarning( _T( "无法删除“设置”对话框的图标: " ) + settingsOnFilename );
        }
      }
      if( !settingsOffFilename.IsEmpty() && wxFileExists( settingsOffFilename ) ) {
        if( !wxRemoveFile( settingsOffFilename ) ) {
          Manager::Get()->GetLogManager()->LogWarning( _T( "无法删除“设置”对话框的图标: " ) + settingsOffFilename );
        }
      }
      return true;
    } else {
      Manager::Get()->GetLogManager()->LogWarning( _T( "删除插件文件失败: " ) + pluginFilename );
      InfoWindow::Tip( _T( "无法完全卸载插件 因为无法删除其文件:\n\n" ) + pluginFilename + _T( '\n' ) + resourceFilename + _T( '\n' ) +
                       settingsOnFilename + _T( '\n' ) + settingsOffFilename, _T( "错误" ), 3500 );
      return false;
    }
  }
  return false;
}

bool PluginManager::ExportPlugin( M_Code_Plugin* plugin, const wxString& filename ) {
  if( !plugin ) {
    return false;
  }
  wxArrayString sourcefiles;
  wxArrayString extrafiles;
  wxArrayString extrafilesdest;
  wxFileName fname;
  wxString resourceFilename;
  for( size_t i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* elem = m_Plugins[i];
    if( elem && elem->plugin == plugin ) {
      sourcefiles.Add( elem->fileName );
      fname.Assign( elem->fileName );
      resourceFilename = fname.GetName() + _T( ".zip" );
      if( true && resourceFilename.StartsWith( _T( "lib" ) ) ) {
        resourceFilename.Remove( 0, 3 );
      }
      resourceFilename = ConfigManager::LocateDataFile( resourceFilename, sdDataGlobal | sdDataUser );
      sourcefiles.Add( resourceFilename );
      resourceFilename = fname.GetName() + _T( ".png" );
      if( true && resourceFilename.StartsWith( _T( "lib" ) ) ) {
        resourceFilename.Remove( 0, 3 );
      }
      resourceFilename.Prepend( _T( "images/settings/" ) );
      resourceFilename = ConfigManager::LocateDataFile( resourceFilename, sdDataGlobal | sdDataUser );
      if( !resourceFilename.IsEmpty() ) {
        sourcefiles.Add( resourceFilename );
      }
      resourceFilename = fname.GetName() + _T( "-off.png" );
      if( true && resourceFilename.StartsWith( _T( "lib" ) ) ) {
        resourceFilename.Remove( 0, 3 );
      }
      resourceFilename.Prepend( _T( "images/settings/" ) );
      resourceFilename = ConfigManager::LocateDataFile( resourceFilename, sdDataGlobal | sdDataUser );
      if( !resourceFilename.IsEmpty() ) {
        sourcefiles.Add( resourceFilename );
      }
      resourceFilename = fname.GetName() + _T( ".zip" );
      if( true && resourceFilename.StartsWith( _T( "lib" ) ) ) {
        resourceFilename.Remove( 0, 3 );
      }
      break;
    }
  }
  if( wxFileExists( filename ) ) {
    if( !wxFile::Access( filename, wxFile::write ) ) {
      InfoWindow::Tip( F( _T( "%s 正在使用中以中止..." ), filename.c_str() ), _T( "错误" ), 3500 );
      return false;
    }
  }
  wxFileOutputStream out( filename );
  wxZipOutputStream zip( out, 9 );
  for( size_t i = 0; i < sourcefiles.GetCount(); ++i ) {
    if( sourcefiles[i].IsEmpty() ) {
      continue;
    }
    wxFileInputStream in( sourcefiles[i] );
    zip.PutNextEntry( wxFileName( sourcefiles[i] ).GetFullName() );
    zip << in;
  }
  for( size_t i = 0; i < extrafiles.GetCount(); ++i ) {
    if( extrafiles[i].IsEmpty() || extrafilesdest[i].IsEmpty() ) {
      continue;
    }
    wxFileInputStream in( extrafiles[i] );
    zip.PutNextEntry( extrafilesdest[i] );
    zip << in;
  }
  zip.SetComment( _T( "这个压缩包是 m_code 的发行插件." ) );
  return true;
}

bool PluginManager::ExtractFile( const wxString& bundlename, const wxString& src_filename,
                                 const wxString& dst_filename, bool isMandatory ) {
  if( wxFileExists( dst_filename ) && !wxFile::Access( dst_filename, wxFile::write ) ) {
    InfoWindow::Tip( _T( "目标文件正在使用中已中止" ), _T( "错误" ), 3500 );
    return false;
  }
  CreateDirRecursively( wxFileName( dst_filename ).GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR ) );
  wxFileSystem* fs = new wxFileSystem;
  wxFSFile* f = fs->OpenFile( bundlename + _T( "#zip:" ) + src_filename );
  if( f ) {
    wxFile output( dst_filename, wxFile::write );
    if( !output.IsOpened() ) {
      wxString msg = F( _T( "无法打开目标文件 '%s' " ), dst_filename.c_str() );
      InfoWindow::Tip( msg, _T( "错误" ), 3500 );
      delete f;
      delete fs;
      return false;
    }
    wxInputStream* is = f->GetStream();
    char tmp[1025] = {};
    while( !is->Eof() && is->CanRead() ) {
      memset( tmp, 0, sizeof( tmp ) );
      is->Read( tmp, sizeof( tmp ) - 1 );
      output.Write( tmp, is->LastRead() );
    }
    delete f;
  } else {
    if( isMandatory ) {
      wxString msg = F( _T( "文件 '%s' 在插件中找不到 '%s'" ), src_filename.c_str(), bundlename.c_str() );
      InfoWindow::Tip( msg, _T( "错误" ), 3500 );
      delete fs;
      return false;
    }
  }
  delete fs;
  return true;
}

void PluginManager::RegisterPlugin( const wxString& name, CreatePluginProc createProc, FreePluginProc freeProc, PluginSDKVersionProc versionProc ) {
  if( name.IsEmpty() || !createProc || !freeProc || !versionProc ) {
    return;
  }
  if( FindPluginByName( name ) ) {
    return;
  }
  PluginInfo info;
  if( !ReadManifestFile( m_CurrentlyLoadingFilename, name, &info ) || info.name.IsEmpty() ) {
    Manager::Get()->GetLogManager()->LogError( _T( "未在清单列表: " ) + name );
    return;
  }
  PluginRegistration pr;
  pr.name = name;
  pr.createProc = createProc;
  pr.freeProc = freeProc;
  pr.versionProc = versionProc;
  pr.info = info;
  m_RegisteredPlugins.push_back( pr );
}

bool PluginManager::ReadManifestFile( const wxString& pluginFilename, const wxString& pluginName, PluginInfo* infoOut ) {
  if( !m_pCurrentlyLoadingManifestDoc ) {
    wxFileName fname( pluginFilename );
    fname.SetExt( _T( "zip" ) );
    wxString actual = fname.GetFullName();
    if( actual.StartsWith( _T( "lib" ) ) ) {
      actual.Remove( 0, 3 );
    }
    actual = ConfigManager::LocateDataFile( actual, sdPluginsUser | sdDataUser | sdPluginsGlobal | sdDataGlobal );
    if( actual.IsEmpty() ) {
      Manager::Get()->GetLogManager()->LogError( _T( "找不到插件资源: " ) + fname.GetFullName() );
      return false;
    }
    wxString contents;
    wxFileSystem* fs = new wxFileSystem;
    wxFSFile* f = fs->OpenFile( actual + _T( "#zip:manifest.xml" ) );
    if( f ) {
      wxInputStream* is = f->GetStream();
      char tmp[1024] = {};
      while( !is->Eof() && is->CanRead() ) {
        memset( tmp, 0, sizeof( tmp ) );
        is->Read( tmp, sizeof( tmp ) - 1 );
        contents << cbC2U( ( const char* )tmp );
      }
      delete f;
    } else {
      Manager::Get()->GetLogManager()->LogError( _T( "资源中没有插件清单文件: " ) + actual );
      delete fs;
      return false;
    }
    delete fs;
    m_pCurrentlyLoadingManifestDoc = new TiXmlDocument;
    if( !m_pCurrentlyLoadingManifestDoc->Parse( cbU2C( contents ) ) ) {
      Manager::Get()->GetLogManager()->LogError( _T( "无法分析插件清单: " ) + actual );
      return false;
    }
  }
  TiXmlElement* root = m_pCurrentlyLoadingManifestDoc->FirstChildElement( "m_Code_plugin_manifest_file" );
  if( !root ) {
    Manager::Get()->GetLogManager()->LogError( _T( "无效的清单文件: " ) + pluginFilename );
    return false;
  }
  if( pluginName.IsEmpty() || !infoOut ) {
    return true;
  }
  TiXmlElement* plugin = root->FirstChildElement( "Plugin" );
  while( plugin ) {
    const char* name = plugin->Attribute( "name" );
    if( name && cbC2U( name ) == pluginName ) {
      infoOut->name = pluginName;
      TiXmlElement* value = plugin->FirstChildElement( "Value" );
      while( value ) {
        if( value->Attribute( "title" ) ) {
          infoOut->title = cbC2U( value->Attribute( "title" ) );
        }
        if( value->Attribute( "description" ) ) {
          infoOut->description = cbC2U( value->Attribute( "description" ) );
        }
        value = value->NextSiblingElement( "Value" );
      }
      break;
    }
    plugin = plugin->NextSiblingElement( "Plugin" );
  }
  return true;
}

int PluginManager::ScanForPlugins( const wxString& path ) {
  int count = 0;
  if( !wxDirExists( path ) ) {
    return count;
  }
  wxDir dir( path );
  if( !dir.IsOpened() ) {
    return count;
  }
  bool batch = Manager::IsBatchBuild();
  wxArrayString bbplugins;
  if( batch ) {
    bbplugins = cbReadBatchBuildPlugins();
  }
  wxString filename, failed, pluginfile;
  bool ok = dir.GetFirst( &filename, _T( "*.so" ), wxDIR_FILES );
  while( ok ) {
    if( batch ) {
      bool matched = false;
      for( size_t i = 0; i < bbplugins.GetCount(); ++i ) {
        if( bbplugins[i] == filename ) {
          matched = true;
          break;
        }
      }
      if( !matched ) {
        ok = dir.GetNext( &filename );
        continue;
      }
    }
    m_pCurrentlyLoadingManifestDoc = nullptr;
    if( ReadManifestFile( filename ) ) {
      if( LoadPlugin( path + wxFILE_SEP_PATH + filename ) ) {
        ++count;
      } else
      { failed << _T( '\n' ) << filename; }
    }
    if( m_pCurrentlyLoadingManifestDoc ) {
      delete m_pCurrentlyLoadingManifestDoc;
      m_pCurrentlyLoadingManifestDoc = nullptr;
    }
    ok = dir.GetNext( &filename );
  }
  Manager::Get()->GetLogManager()->Log( F( _T( "加载 %d 个插件" ), count ) );
  if( !failed.IsEmpty() ) {
    InfoWindow::Tip( _T( "插件未能加载:\n" ) + failed, _T( "错误" ), 15000, 3000 );
  }
  return count;
}

bool PluginManager::LoadPlugin( const wxString& pluginName ) {
  m_RegisteredPlugins.clear();
  m_CurrentlyLoadingFilename = pluginName;
  m_pCurrentlyLoadingLib = LibLoader::LoadLibrary( pluginName );
  if( !m_pCurrentlyLoadingLib->IsLoaded() ) {
    Manager::Get()->GetLogManager()->LogError( F( _T( "%s: 未加载（缺少符号）" ), pluginName.wx_str() ) );
    LibLoader::RemoveLibrary( m_pCurrentlyLoadingLib );
    m_pCurrentlyLoadingLib = nullptr;
    m_CurrentlyLoadingFilename.Clear();
    return false;
  }
  std::vector<PluginRegistration>::iterator it;
  for( it = m_RegisteredPlugins.begin(); it != m_RegisteredPlugins.end(); ++it ) {
    PluginRegistration& pr = *it;
    M_Code_Plugin* plug = nullptr;
    try {
      plug = pr.createProc();
    } catch( M_Code_Exception& exception ) {
      exception.ShowErrorMessage( false );
      continue;
    }
    PluginElement* plugElem = new PluginElement;
    plugElem->fileName = m_CurrentlyLoadingFilename;
    plugElem->info = pr.info;
    plugElem->library = m_pCurrentlyLoadingLib;
    plugElem->freeProc = pr.freeProc;
    plugElem->plugin = plug;
    m_Plugins.Add( plugElem );
    if( plug->GetType() == ptCompiler ) {
      m_CompilerPlugins.push_back( static_cast<cbCompilerPlugin*>( plug ) );
    }
    SetupLocaleDomain( pr.name );
  }
  if( m_RegisteredPlugins.empty() ) {
    LibLoader::RemoveLibrary( m_pCurrentlyLoadingLib );
  }
  m_pCurrentlyLoadingLib = nullptr;
  m_CurrentlyLoadingFilename.Clear();
  return true;
}

void PluginManager::LoadAllPlugins() {
  wxString probPlugin = Manager::Get()->GetConfigManager( _T( "plugins" ) )->Read( _T( "/try_to_activate" ), wxEmptyString );
  unsigned int plugins = -1;
  if( !probPlugin.IsEmpty() ) {
    wxString msg;
    msg.Printf( _T( "插件 \"%s\" 上次运行失败是否要禁止加载此插件?" ), probPlugin.c_str() );
    if( msging( msg, _T( "警告" ), wxICON_WARNING | wxYES_NO ) == wxID_NO ) {
      probPlugin = _T( "" );
    }
  }
  PluginElement* elem = nullptr;
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    elem = m_Plugins[i];
    M_Code_Plugin* plug = elem->plugin;
    if( !plug || plug->IsAttached() ) {
      continue;
    }
    wxString baseKey;
    baseKey << _T( "/" ) << elem->info.name;
    bool loadIt = Manager::Get()->GetConfigManager( _T( "plugins" ) )->ReadBool( baseKey, true );
    if( loadIt && !probPlugin.IsEmpty() ) {
      loadIt = elem->info.title != probPlugin;
      if( !loadIt ) {
        Manager::Get()->GetConfigManager( _T( "plugins" ) )->Write( baseKey, false );
      }
    }
    if( loadIt ) {
      Manager::Get()->GetConfigManager( _T( "plugins" ) )->Write( _T( "/try_to_activate" ), elem->info.title );
      Manager::Get()->GetLogManager()->Log( elem->info.name + _T( " ------ 载入成功" ) );
      plugins++;
      try {
        AttachPlugin( plug );
        Manager::Get()->GetConfigManager( _T( "plugins" ) )->Write( _T( "/try_to_activate" ), wxEmptyString, false );
      } catch( M_Code_Exception& exception ) {
        plugins--;
        Manager::Get()->GetLogManager()->LogError( _T( "加载插件出现异常" ) );
        exception.ShowErrorMessage( false );
        wxString msg;
        msg.Printf( _T( "插件 \"%s\" 加载失败禁止下次加载此插件?" ), elem->info.title.c_str() );
        if( msging( msg, _T( "警告" ), wxICON_WARNING | wxYES_NO ) == wxID_YES ) {
          Manager::Get()->GetConfigManager( _T( "plugins" ) )->Write( baseKey, false );
        }
      }
    }
  }
  Manager::Get()->GetLogManager()->Log( F( _T( " %d 个插件加载成功" ), plugins ) );
  Manager::Get()->GetConfigManager( _T( "plugins" ) )->Write( _T( "/try_to_activate" ), wxEmptyString, false );
}

void PluginManager::UnloadAllPlugins() {
  while( m_Plugins.GetCount() ) {
    UnloadPlugin( m_Plugins[0]->plugin );
  }
  m_CompilerPlugins.clear();
  m_Plugins.Clear();
  LibLoader::Cleanup();
}

void PluginManager::UnloadPlugin( M_Code_Plugin* plugin ) {
  if( !plugin ) {
    return;
  }
  DetachPlugin( plugin );
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* plugElem = m_Plugins[i];
    if( plugElem->plugin == plugin ) {
      if( plugin->GetType() == ptCompiler ) {
        auto removeIter = std::remove( m_CompilerPlugins.begin(), m_CompilerPlugins.end(), plugin );
        if( removeIter != m_CompilerPlugins.end() ) {
          m_CompilerPlugins.erase( removeIter );
        }
      }
      if( plugElem->freeProc ) {
        plugElem->freeProc( plugin );
      } else {
        delete plugin;
      }
      LibLoader::RemoveLibrary( plugElem->library );
      delete plugElem;
      m_Plugins.RemoveAt( i );
      break;
    }
  }
}

PluginElement* PluginManager::FindElementByName( const wxString& pluginName ) {
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* plugElem = m_Plugins[i];
    if( plugElem->info.name == pluginName ) {
      return plugElem;
    }
  }
  return nullptr;
}

M_Code_Plugin* PluginManager::FindPluginByName( const wxString& pluginName ) {
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* plugElem = m_Plugins[i];
    if( plugElem->info.name == pluginName ) {
      return plugElem->plugin;
    }
  }
  return nullptr;
}

M_Code_Plugin* PluginManager::FindPluginByFileName( const wxString& pluginFileName ) {
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* plugElem = m_Plugins[i];
    if( plugElem->fileName == pluginFileName ) {
      return plugElem->plugin;
    }
  }
  return nullptr;
}

const PluginInfo* PluginManager::GetPluginInfo( const wxString& pluginName ) {
  PluginElement* plugElem = FindElementByName( pluginName );
  if( plugElem && plugElem->info.name == pluginName ) {
    return &plugElem->info;
  }
  return nullptr;
}

const PluginInfo* PluginManager::GetPluginInfo( M_Code_Plugin* plugin ) {
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    PluginElement* plugElem = m_Plugins[i];
    if( plugElem->plugin == plugin ) {
      return &plugElem->info;
    }
  }
  return nullptr;
}

int PluginManager::ExecutePlugin( const wxString& pluginName ) {
  PluginElement* elem = FindElementByName( pluginName );
  M_Code_Plugin* plug = elem ? elem->plugin : nullptr;
  if( plug ) {
    if( plug->GetType() != ptTool ) {
      Manager::Get()->GetLogManager()->LogError( F( _T( "插件 %s 不具有 Execute() 方法!" ), elem->info.name.wx_str() ) );
    } else {
      try {
        return ( ( cbToolPlugin* )plug )->Execute();
      } catch( M_Code_Exception& exception ) {
        exception.ShowErrorMessage( false );
      }
    }
  } else {
    Manager::Get()->GetLogManager()->LogError( F( _T( "没有使用此名称注册的插件: %s" ), pluginName.wx_str() ) );
  }
  return 0;
}

inline int SortByConfigurationPriority( M_Code_Plugin** first, M_Code_Plugin** second ) {
  return ( *first )->GetConfigurationPriority() - ( *second )->GetConfigurationPriority();
}

void PluginManager::GetConfigurationPanels( int group, wxWindow* parent, ConfigurationPanelsArray& arrayToFill ) {
  PluginsArray arr;
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    M_Code_Plugin* plug = m_Plugins[i]->plugin;
    if( plug && plug->IsAttached() && ( plug->GetConfigurationGroup() & group ) ) {
      arr.Add( plug );
    }
  }
  arr.Sort( SortByConfigurationPriority );
  arrayToFill.Clear();
  for( unsigned int i = 0; i < arr.GetCount(); ++i ) {
    M_Code_Plugin* plug = arr[i];
    cbConfigurationPanel* pnl = plug->GetConfigurationPanel( parent );
    if( pnl ) {
      arrayToFill.Add( pnl );
    }
  }
}

void PluginManager::GetProjectConfigurationPanels( wxWindow* parent, M_Code_Project* project, ConfigurationPanelsArray& arrayToFill ) {
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    M_Code_Plugin* plug = m_Plugins[i]->plugin;
    if( plug && plug->IsAttached() ) {
      cbConfigurationPanel* pnl = plug->GetProjectConfigurationPanel( parent, project );
      if( pnl ) {
        arrayToFill.Add( pnl );
      }
    }
  }
}

cbCompilerPlugin* PluginManager::GetFirstCompiler() const {
  if( m_CompilerPlugins.empty() ) {
    return nullptr;
  }
  return m_CompilerPlugins.front();
}

PluginsArray PluginManager::GetToolOffers() {
  return GetOffersFor( ptTool );
}

PluginsArray PluginManager::GetMimeOffers() {
  return GetOffersFor( ptMime );
}

PluginsArray PluginManager::GetDebuggerOffers() {
  return GetOffersFor( ptDebugger );
}

PluginsArray PluginManager::GetCodeCompletionOffers() {
  return GetOffersFor( ptCodeCompletion );
}

PluginsArray PluginManager::GetSmartIndentOffers() {
  return GetOffersFor( ptSmartIndent );
}

PluginsArray PluginManager::GetOffersFor( PluginType type ) {
  PluginsArray arr;
  M_Code_Plugin* dflt = nullptr;
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    M_Code_Plugin* plug = m_Plugins[i]->plugin;
    if( plug && plug->IsAttached() && plug->GetType() == type ) {
      if( type == ptMime ) {
        if( ( ( cbMimePlugin* )plug )->HandlesEverything() ) {
          dflt = plug;
        } else {
          arr.Add( plug );
        }
      } else {
        arr.Add( plug );
      }
    }
  }
  if( dflt ) {
    arr.Add( dflt );
  }
  return arr;
}

void PluginManager::AskPluginsForModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data ) {
  for( unsigned int i = 0; i < m_Plugins.GetCount(); ++i ) {
    M_Code_Plugin* plug = m_Plugins[i]->plugin;
    if( plug && plug->IsAttached() ) {
      try {
        plug->BuildModuleMenu( type, menu, data );
      } catch( M_Code_Exception& exception ) {
        exception.ShowErrorMessage( false );
      }
    }
  }
  wxArrayInt ids = ScriptBindings::ScriptPluginWrapper::CreateModuleMenu( type, menu, data );
  for( size_t i = 0; i < ids.GetCount(); ++i ) {
    Connect( ids[i], -1, wxEVT_COMMAND_MENU_SELECTED, ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
             &PluginManager::OnScriptModuleMenu );
  }
}

void PluginManager::OnScriptMenu( wxCommandEvent& event ) {
  ScriptBindings::ScriptPluginWrapper::OnScriptMenu( event.GetId() );
}

void PluginManager::OnScriptModuleMenu( wxCommandEvent& event ) {
  ScriptBindings::ScriptPluginWrapper::OnScriptModuleMenu( event.GetId() );
}

cbMimePlugin* PluginManager::GetMIMEHandlerForFile( const wxString& filename ) {
  PluginsArray mimes = GetMimeOffers();
  for( unsigned int i = 0; i < mimes.GetCount(); ++i ) {
    cbMimePlugin* plugin = ( cbMimePlugin* )mimes[i];
    if( plugin && plugin->CanHandleFile( filename ) ) {
      return plugin;
    }
  }
  return nullptr;
}

int PluginManager::Configure() {
  PluginsConfigurationDlg dlg( Manager::Get()->GetAppWindow() );
  PlaceWindow( &dlg );
  return dlg.ShowModal();
}

void PluginManager::SetupLocaleDomain( const wxString& DomainName ) {
  int catalogNum = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/locale/catalogNum" ), ( int )0 );
  int i = 1;
  for( ; i <= catalogNum; ++i ) {
    wxString catalogName = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( F( _T( "/locale/Domain%d" ), i ), wxEmptyString );
    if( catalogName.Cmp( DomainName ) == 0 ) {
      break;
    }
  }
  if( i > catalogNum ) {
    ++catalogNum;
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/locale/catalogNum" ), ( int )catalogNum );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( F( _T( "/locale/Domain%d" ), i ), DomainName );
  }
}

void PluginManager::NotifyPlugins( CodeBlocksEvent& event ) {
  Manager::Get()->ProcessEvent( event );
}

void PluginManager::NotifyPlugins( CodeBlocksDockEvent& event ) {
  Manager::Get()->ProcessEvent( event );
}

void PluginManager::NotifyPlugins( CodeBlocksLayoutEvent& event ) {
  Manager::Get()->ProcessEvent( event );
}

bool cbHasRunningCompilers( const PluginManager *manager ) {
  for( const cbCompilerPlugin *p : manager->GetCompilerPlugins() ) {
    if( p && p->IsRunning() ) {
      return true;
    }
  }
  return false;
}

void cbStopRunningCompilers( PluginManager *manager ) {
  for( cbCompilerPlugin *compiler : manager->GetCompilerPlugins() ) {
    if( !compiler || !compiler->IsRunning() ) {
      continue;
    }
    compiler->KillProcess();
    while( compiler->IsRunning() ) {
      wxMilliSleep( 100 );
      Manager::Yield();
    }
  }
}
